/*
 * File      : clock.c
 * This file is part of FH8620 BSP for RT-Thread distribution.
 *
 * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
 * All rights reserved
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Visit http://www.fullhan.com to get contact with Fullhan.
 *
 * Change Logs:
 * Date           Author       Notes
 */

#include "clock.h"
#include <rtdevice.h>
#include "fh_arch.h"
#include "Libraries/inc/fh_timer.h"
#include "fh_pmu.h"
//#include "chip_reg.h"
// NEED_CAUTION.

#define TIMER_CLOCK 1000000
#define FH_CLK_DEBUG

#define FH_CLK_DIV_DEFAULT
//#define FH_CLK_GATE_DEFAULT

//#define FH_DBG_CLK

#define FH_CLK_DIV_DEFAULT_VALUE 0x55aaaa55
#define FH_CLK_GATE_DEFAULT_VALUE 0xaa5555aa

#define CONFIG_PAE_PTS_CLOCK (1000000)
#define TICKS_PER_USEC (CONFIG_PAE_PTS_CLOCK / 1000000)
#define REG_PAE_PTS_REG (0xec100000 + 0x0040)

#define fh_clk_err(p, fmt, args...) \
    rt_kprintf("clk_err: %s->\t" fmt, p->name, ##args)

#if defined(FH_CLK_DEBUG) && defined(RT_DEBUG)
#define fh_clk_debug(p, fmt, args...) rt_kprintf("%s:\t\t" fmt, p->name, ##args)

#define fh_clk_debug_no_handle(fmt, args...) rt_kprintf(fmt, ##args)
#else
//#define fh_clk_err(p,fmt,args...)
#define fh_clk_debug(p, fmt, args...)
#define fh_clk_debug_no_handle(fmt, args...)
#endif

struct fh_clk_tree;

static struct fh_clk_tree fh_clk_tree;

extern int __rt_ffs(int value);
#define __raw_writel(v, a) (*(volatile unsigned int*)(a) = (v))
#define __raw_readl(a) (*(volatile unsigned int*)(a))

#define FH_TIMER_WRITEL(offset, value) \
    __raw_writel(value, (fh_clk_tree.c_base_addr + offset))
#define FH_TIMER_READL(offset) __raw_readl((fh_clk_tree.c_base_addr + offset))

enum clk_gate_enum
{
#define CLK_GATE (1)
#define CLK_UNGATE (0)
    ISP_ACLK_GATE  = (1 << 0),
    HCLK_GATE      = (1 << 1),
    CPU_FCLK0_GATE = (1 << 3),
    VCU_CLK_GATE   = (1 << 4),
    VOU_CLK_GATE   = (1 << 5),
    MCLK_GATE      = (1 << 6),
    SPI0_CLK_GATE  = (1 << 7),
    SPI1_CLK_GATE  = (1 << 8),
    SDC0_CLK_GATE  = (1 << 9),
    SDC1_CLK_GATE  = (1 << 10),
    AC_MCLK_GATE   = (1 << 11),  /////
    I2C0_CLK_GATE  = (1 << 12),
    UART0_CLK_GATE = (1 << 13),
    UART1_CLK_GATE = (1 << 14),
    // can't change
    WDT_CLK_GATE = (1 << 15),

    PWM_CLK_GATE          = (1 << 16),
    TMR0_CLK_GATE         = (1 << 17),
    TMR1_CLK_GATE         = (1 << 18),
    PTS_CLK_GATE          = (1 << 19),
    MIPI_DPHY_CLK20M_GATE = (1 << 20),
    MIPI_P32_CLK_GATE     = (1 << 21),
    PIX_CLK_GATE          = (1 << 22),  ////
    CIS_CLK_OUT_GATE      = (1 << 23),
    I2S_SCLK_GATE         = (1 << 24),  //////
    ETH_REF_CLK_GATE      = (1 << 25),
    SADC_CLK_GATE         = (1 << 26),
    I2C1_CLK_GATE         = (1 << 27),
    ETH_RX_CLK_GATE       = (1 << 28),  /////
    ETH_TX_CLK_GATE       = (1 << 29),  /////
    ETH_RMII_CLK_GATE     = (1 << 30),  ////

};

// struct fh_clk;

typedef void (*clk_update)(struct fh_clk* p_clk);

// update func...
void clk_in_update(struct fh_clk* p_clk);
void pll1_clk_update(struct fh_clk* p_clk);
void pll0_clk_update(struct fh_clk* p_clk);
void cis_pclk_update(struct fh_clk* p_clk);
void ddr_clk_update(struct fh_clk* p_clk);
void ddr_clk_update(struct fh_clk* p_clk);
void fclk_update(struct fh_clk* p_clk);
void aclk_update(struct fh_clk* p_clk);
void hclk_update(struct fh_clk* p_clk);
void pclk_update(struct fh_clk* p_clk);
void isp_aclk_update(struct fh_clk* p_clk);
void vcu_clk_update(struct fh_clk* p_clk);
void vou_clk_update(struct fh_clk* p_clk);
void mipi_p32_clk_update(struct fh_clk* p_clk);
void cis_clk_out_update(struct fh_clk* p_clk);
void pts_update(struct fh_clk* p_clk);
void mipi_pix_clk_update(struct fh_clk* p_clk);
void spi0_clk_update(struct fh_clk* p_clk);
void spi1_clk_update(struct fh_clk* p_clk);
void mipi_dphy_clk20m_update(struct fh_clk* p_clk);
void i2c0_clk_update(struct fh_clk* p_clk);
void i2c1_clk_update(struct fh_clk* p_clk);
void uart0_clk_update(struct fh_clk* p_clk);
void uart1_clk_update(struct fh_clk* p_clk);
void pwm_clk_update(struct fh_clk* p_clk);
void time0_clk_update(struct fh_clk* p_clk);
void time1_clk_update(struct fh_clk* p_clk);
void sadc_clk_update(struct fh_clk* p_clk);
void sdc0_clk2x_update(struct fh_clk* p_clk);
void sdc0_clk_update(struct fh_clk* p_clk);
void sdc0_clk_out_update(struct fh_clk* p_clk);
void sdc0_clk_sample_update(struct fh_clk* p_clk);
void sdc0_clk_drv_update(struct fh_clk* p_clk);
void sdc1_clk2x_update(struct fh_clk* p_clk);
void sdc1_clk_update(struct fh_clk* p_clk);
void sdc1_clk_out_update(struct fh_clk* p_clkt);
void sdc1_clk_sample_update(struct fh_clk* p_clk);
void sdc1_clk_drv_update(struct fh_clk* p_clk);
void eth_ref_clk_update(struct fh_clk* p_clk);
void wdt_clk_update(struct fh_clk* p_clk);

rt_int32_t check_pix_clk_source(rt_uint32_t offset, rt_uint32_t mask,
                                rt_uint32_t* value);
void pix_update(struct fh_clk* p_clk);

struct fh_clk_div
{
// some has prediv....
// this two could have or......
#define PRE_DIV_CAL_ALREADY (0x80000000)
#define PRE_DIV_ENABLE (0x01)
#define DIV_ENABLE (0x10)
    rt_uint32_t div_flag;

    rt_uint32_t pdiv_value;

    // rt_uint32_t hw_div_value;
    rt_uint32_t sw_div_value;
    rt_uint32_t sw_div_multi;
    // rt_uint32_t clk_in_hz;
    rt_uint32_t reg_offset;
    rt_uint32_t reg_mask;
    // rt_uint32_t rate;
};

struct fh_clk_mux
{
//#define MUX_LEVEL_1		(1)
//#define MUX_LEVEL_2		(2)
//#define MAX_MUX_LEVEL	MUX_LEVEL_2
//	rt_uint32_t lev;
#define HAS_MUX (0)
#define HAS_NO_MUX (1)
    rt_uint32_t mux_flag;
    rt_uint32_t hw_mux_value;
    rt_uint32_t sw_mux_value;
    rt_uint32_t reg_offset;
    rt_uint32_t reg_mask;
};

struct fh_clk_gate
{
#define HAS_GATE (0)
#define HAS_NO_GATE (1)
    rt_uint32_t gate_flag;
#define CLK_UNGATE (0)
#define CLK_GATE (1)
    // rt_uint32_t	hw_status;
    rt_uint32_t sw_status;
    // rt_uint32_t value;

    rt_uint32_t reg_offset;
    rt_uint32_t reg_mask;
};

/***************
 *
 * level 1
 *
 ***************/
struct fh_clk_level_1
{
    rt_uint32_t clk_in_out;
};

/***************
 *
 * level 2
 *
 ***************/
struct fh_clk_level_2
{
    rt_uint32_t clk_in_out;
};

/***************
 *
 * level 3
 *
 ***************/
struct fh_clk_level_3_ddr
{
    // rt_uint32_t mux_level;

    struct fh_clk_mux mux[2];
    struct fh_clk_gate gate;
    struct fh_clk_div div;
};

struct fh_clk_level_3_sdc
{
#define DIFF_REFERENCE (0x80000000)

    rt_uint32_t phase_diff;
    rt_uint32_t reg_offset;
    rt_uint32_t reg_mask;
};

struct fh_clk_level_3_gmac
{
};

struct fh_clk_level_3_normal
{
    struct fh_clk_mux mux;
    struct fh_clk_gate gate;
    struct fh_clk_div div;
};

struct fh_clk_level_3
{
#define LEVEL_PERI_NORMAL (0x301)
#define LEVEL_PERI_DDR (0x302)
#define LEVEL_PERI_SDC (0x303)
#define LEVEL_PERI_GMAC (0x304)
    rt_uint32_t peri_flag;
    union {
        struct fh_clk_level_3_ddr ddr;
        struct fh_clk_level_3_sdc sdc;
        struct fh_clk_level_3_gmac gmac;
        struct fh_clk_level_3_normal normal;
    } obj;
};

struct fh_clk
{
    char* name;
#define LEVEL_CRYSTAL (0x100)
#define LEVEL_PLL (0x200)
#define LEVEL_PERIPHERAL (0x300)
    rt_uint32_t level;

#define ROOT_NODE (RT_NULL)
    struct fh_clk* parent;

    union {
        struct fh_clk_level_1 crystal;
        struct fh_clk_level_2 pll;
        struct fh_clk_level_3 peri;
    } clk;

    rt_uint32_t clk_out_rate;
#define CLK_HAS_NO_GATE (0x80000000)
    rt_uint32_t gate;

    clk_update update_func;

    // struct fh_clk_tree *p_tree;
};

struct fh_clk_tree
{
    rt_uint32_t c_base_addr;
    struct fh_clk** clk_head;
};

/*********
 *
 *
 * clk map....
 *
 *
 ********/
#define CRYSTAL_HZ (24000000)
struct fh_clk clk_in = {
    .name                   = "clk_in",
    .level                  = LEVEL_CRYSTAL,
    .parent                 = ROOT_NODE,
    .clk.crystal.clk_in_out = CRYSTAL_HZ,
    //.clk_out_rate = clk_in.clk.crystal.clk_in_out,
    .clk_out_rate = CRYSTAL_HZ,
    .update_func  = clk_in_update,
};

#define CIS_PCLK_HZ (108000000)
struct fh_clk cis_pclk = {
    .name                   = "cis_pclk",
    .level                  = LEVEL_CRYSTAL,
    .parent                 = ROOT_NODE,
    .clk.crystal.clk_in_out = CIS_PCLK_HZ,
    //.clk_out_rate = clk_in.clk.crystal.clk_in_out,
    .clk_out_rate = CIS_PCLK_HZ,
    .update_func  = cis_pclk_update,
};

#define PLL0_HZ (864000000)
struct fh_clk pll0 = {
    .name                   = "pll0",
    .level                  = LEVEL_PLL,
    .parent                 = &clk_in,
    .clk.crystal.clk_in_out = PLL0_HZ,
    //.clk_out_rate = pll0.clk.crystal.clk_in_out,
    .clk_out_rate = PLL0_HZ,
    .update_func  = pll0_clk_update,
};

#define PLL1_HZ (600000000)
struct fh_clk pll1 = {
    .name                   = "pll1",
    .level                  = LEVEL_PLL,
    .parent                 = &clk_in,
    .clk.crystal.clk_in_out = PLL1_HZ,
    .clk_out_rate           = PLL1_HZ,
    .update_func            = pll1_clk_update,

};

// NEED_CAUTION   parent not fix...
static struct fh_clk ddr_clk_normal = {
    .name  = "ddr_normal",
    .level = LEVEL_PERIPHERAL,
    //.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_DDR,
// 0:xtal_clk
// 1:pll0_clk
#define MUX0_XTAL_CLK (0)
#define MUX0_PLL0_CLK (1)

    .clk.peri.obj.ddr.mux[0].reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.ddr.mux[0].reg_mask   = 1 << 0,

// 0:pll0 clk  default 864/2M
// 1:pll1 clk  default 600M
#define MUX1_PLL0_CLK (0)
#define MUX1_PLL1_CLK (1)
    .clk.peri.obj.ddr.mux[1].reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.ddr.mux[1].reg_mask   = 1 << 24,

// gate
//.clk.peri.obj.ddr.gate.enable_status = CLK_ENABLE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.ddr.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.ddr.gate.sw_status      = CLK_UNGATE,
#endif
    .clk.peri.obj.ddr.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.ddr.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.ddr.gate.reg_mask   = MCLK_GATE,

    // div
    // clk in maybe cry or pll
    .clk.peri.obj.ddr.div.div_flag = DIV_ENABLE,
//.clk.peri.obj.ddr.div.pdiv_value = 2,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.ddr.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.ddr.div.sw_div_value    = 1,
#endif
    .clk.peri.obj.ddr.div.sw_div_multi = 1,
    .clk.peri.obj.ddr.div.reg_offset   = REG_PMU_CLK_DIV1,
    .clk.peri.obj.ddr.div.reg_mask     = 0xff << 0,

    .update_func = ddr_clk_update,
};

// NEED_CAUTION   parent not fix...
static struct fh_clk ddr_clk_div2 = {
    .name  = "ddr_div2",
    .level = LEVEL_PERIPHERAL,
    //.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_DDR,
    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.ddr.mux[0].reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.ddr.mux[0].reg_mask   = 1 << 0,

    // 0:pll0 clk  default 864/2M
    // 1:pll1 clk  default 600M
    .clk.peri.obj.ddr.mux[1].reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.ddr.mux[1].reg_mask   = 1 << 24,

// gate
//.clk.peri.obj.ddr.gate.enable_status = CLK_ENABLE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.ddr.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.ddr.gate.sw_status      = CLK_UNGATE,
#endif
    .clk.peri.obj.ddr.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.ddr.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.ddr.gate.reg_mask   = MCLK_GATE,

    // div
    // clk in maybe cry or pll
    .clk.peri.obj.ddr.div.div_flag   = PRE_DIV_ENABLE | DIV_ENABLE,
    .clk.peri.obj.ddr.div.pdiv_value = 2,
#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.ddr.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.ddr.div.sw_div_value    = 1,
#endif
    .clk.peri.obj.ddr.div.sw_div_multi = 1,
    .clk.peri.obj.ddr.div.reg_offset   = REG_PMU_CLK_DIV1,
    .clk.peri.obj.ddr.div.reg_mask     = 0xff << 0,

    .update_func = ddr_clk_update,
};

static struct fh_clk cpu_fclk = {
    .name  = "cpu_fclk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE,
    //.clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    //.clk.peri.obj.normal.gate.reg_mask = CPU_FCLK0_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag = DIV_ENABLE,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 0,
#endif

    .clk.peri.obj.normal.div.sw_div_multi = 1,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV0,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 0,

    .update_func = fclk_update,

};

// NEED_CAUTION   parent not fix...
static struct fh_clk cpu_aclk = {
    .name  = "cpu_aclk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,
    //	.clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    //	.clk.peri.obj.normal.mux.reg_mask = 1<<0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = CPU_FCLK0_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag   = PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.pdiv_value = 2,
    //	.clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0,
    //	.clk.peri.obj.normal.div.reg_mask = 0xff << 0,

    .update_func = aclk_update,

};

static struct fh_clk cpu_hclk = {
    .name  = "cpu_hclk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE,
    //.clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    //.clk.peri.obj.normal.gate.reg_mask = CPU_FCLK0_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag = DIV_ENABLE,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif
    .clk.peri.obj.normal.div.sw_div_multi = 1,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV0,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 16,

    .update_func = hclk_update,
};
// NEED_CAUTION   parent not fix...

static struct fh_clk cpu_pclk = {
    .name  = "cpu_pclk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = HCLK_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag   = PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.pdiv_value = 2,
    //	.clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0,
    //	.clk.peri.obj.normal.div.reg_mask = 0xff << 0,

    .update_func = pclk_update,

};

// NEED_CAUTION   parent not fix...
static struct fh_clk isp_aclk = {
    .name  = "isp_aclk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = ISP_ACLK_GATE,
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV0,
    .clk.peri.obj.normal.div.reg_mask     = 0x03 << 8,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .update_func = isp_aclk_update,

};

//
////NEED_CAUTION   parent not fix...
static struct fh_clk vcu_clk = {
    .name  = "vcu_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = VCU_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV0,
    .clk.peri.obj.normal.div.reg_mask     = 0x03 << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .update_func = vcu_clk_update,

};

static struct fh_clk vou_clk = {
    .name  = "vou_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = VOU_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV1,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 8,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .update_func = vou_clk_update,
};

static struct fh_clk mipi_p32_clk = {
    .name  = "mipi_p32_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = MIPI_P32_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV2,
    .clk.peri.obj.normal.div.reg_mask     = 0x0f << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .update_func = mipi_p32_clk_update,

};

static struct fh_clk cis_clk_out = {
    .name  = "cis_clk_out",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = CIS_CLK_OUT_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV1,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 3,
#endif

    .update_func = cis_clk_out_update,

};

static struct fh_clk pts_clk = {
    .name  = "pts_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = PTS_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV2,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 0,
    .clk.peri.obj.normal.div.sw_div_multi = 1,
#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 35,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 12,

    .update_func = pts_update,

};

static struct fh_clk mipi_pix_clk = {
    .name               = "mipi_pix_clk_i",
    .level              = LEVEL_PERIPHERAL,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    // 0:xtal_clk
    // 1:pll0_clk
    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL,
    .clk.peri.obj.normal.mux.reg_mask   = 1 << 0,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV2,
    .clk.peri.obj.normal.div.reg_mask     = 0x0f << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,
#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 3,
#endif

    .update_func = mipi_pix_clk_update,

};

static struct fh_clk pix_clk = {
    .name  = "pix_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

// 0:xtal_clk
// 1:pll0_clk
#define CIS_PIX_CLK (0)
#define CIS_PIX_CLK_OPPOSITE (1)
#define MIPI_PIX_CLK (2)

    .clk.peri.obj.normal.mux.reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.normal.mux.reg_mask   = 3 << 4,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = PIX_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag = 0,

    .update_func = pix_update,

};

static struct fh_clk spi0_clk = {
    .name  = "spi0_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = SPI0_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV3,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 0,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 11,
#endif

    .update_func = spi0_clk_update,

};

static struct fh_clk spi1_clk = {
    .name  = "spi1_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,
    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = SPI1_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV3,
    .clk.peri.obj.normal.div.reg_mask     = 0xff << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 11,
#endif

#ifdef RT_USING_SPI1
    .clk.peri.obj.normal.div.sw_div_value = 11,
#endif

    .update_func = spi1_clk_update,

};

static struct fh_clk mipi_dphy_clk20m = {
    .name  = "mipi_dphy_clk20m",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = MIPI_DPHY_CLK20M_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag = PRE_DIV_ENABLE,
    //	.clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3,
    //	.clk.peri.obj.normal.div.reg_mask = 0xff << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,
    .clk.peri.obj.normal.div.pdiv_value   = 30,
    //	.clk.peri.obj.normal.div.sw_div_value = 11,

    .update_func = mipi_dphy_clk20m_update,
};

static struct fh_clk i2c0_clk = {
    .name  = "i2c0_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = I2C0_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV4,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 20,

    .update_func = i2c0_clk_update,

};

static struct fh_clk i2c1_clk = {
    .name  = "i2c1_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = I2C1_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV4,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif
    .clk.peri.obj.normal.div.pdiv_value = 20,

    .update_func = i2c1_clk_update,
};

static struct fh_clk uart0_clk = {
    .name  = "uart0_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = UART0_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV4,
    .clk.peri.obj.normal.div.reg_mask     = 0x1f << 0,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 10,

    .update_func = uart0_clk_update,

};

static struct fh_clk uart1_clk = {
    .name  = "uart1_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = UART1_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV4,
    .clk.peri.obj.normal.div.reg_mask     = 0x1f << 8,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 1,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 10,

    .update_func = uart1_clk_update,

};

static struct fh_clk pwm_clk = {
    .name  = "pwm_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = PWM_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV5,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 0,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 29,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 20,

    .update_func = pwm_clk_update,
};

static struct fh_clk time0_clk = {
    .name  = "time0_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,
    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = TMR0_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV5,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 16,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 29,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 20,

    .update_func = time0_clk_update,

};

static struct fh_clk time1_clk = {
    .name  = "time1_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,

    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = TMR1_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV5,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 29,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 20,

    .update_func = time1_clk_update,

};

static struct fh_clk sadc_clk = {
    .name  = "sadc_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = SADC_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.sw_div_multi = 1,
    .clk.peri.obj.normal.div.pdiv_value   = 120,

    .update_func = sadc_clk_update,

};

static struct fh_clk sdc0_clk2x = {
    .name  = "sdc0_clk2x",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_GATE,

    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = SDC0_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV3,
    .clk.peri.obj.normal.div.reg_mask     = 0x0f << 8,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 5,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 2,

    .update_func = sdc0_clk2x_update,

};

static struct fh_clk sdc0_clk = {
    .name  = "sdc0_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0 | DIFF_REFERENCE,
    //	.clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    //	.clk.peri.obj.sdc.reg_mask = 0x0,

    .update_func = sdc0_clk_update,

};

static struct fh_clk sdc0_clk_out = {
    .name  = "sdc0_clk_out",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    //	.clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    //	.clk.peri.obj.sdc.reg_mask = 0x0,

    .update_func = sdc0_clk_out_update,
};

static struct fh_clk sdc0_clk_sample = {
    .name  = "sdc0_clk_sample",
    .level = LEVEL_PERIPHERAL,

    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.sdc.reg_mask   = 3 < 16,

    .update_func = sdc0_clk_sample_update,
};

static struct fh_clk sdc0_clk_drive = {
    .name  = "sdc0_clk_drive",
    .level = LEVEL_PERIPHERAL,

    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.sdc.reg_mask   = 3 < 20,

    .update_func = sdc0_clk_drv_update,
};

static struct fh_clk sdc1_clk2x = {
    .name  = "sdc1_clk2x",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = SDC1_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV3,
    .clk.peri.obj.normal.div.reg_mask     = 0x0f << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,
#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 5,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 2,

    .update_func = sdc1_clk2x_update,
};

static struct fh_clk sdc1_clk = {
    .name  = "sdc1_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0 | DIFF_REFERENCE,
    //	.clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    //	.clk.peri.obj.sdc.reg_mask = 0x0,

    .update_func = sdc1_clk_update,
};

static struct fh_clk sdc1_clk_out = {
    .name  = "sdc1_clk_out",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    //	.clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    //	.clk.peri.obj.sdc.reg_mask = 0x0,

    .update_func = sdc1_clk_out_update,
};

static struct fh_clk sdc1_clk_sample = {
    .name  = "sdc1_clk_sample",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.sdc.reg_mask   = 3 < 8,

    .update_func = sdc1_clk_sample_update,
};

static struct fh_clk sdc1_clk_drive = {
    .name  = "sdc1_clk_drive",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag          = LEVEL_PERI_SDC,
    .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0,
    .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL,
    .clk.peri.obj.sdc.reg_mask   = 3 < 12,

    .update_func = sdc1_clk_drv_update,
};

static struct fh_clk eth_ref_clk = {
    .name  = "eth_ref_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag = LEVEL_PERI_NORMAL,

    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag  = HAS_GATE,
    .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE,
    .clk.peri.obj.normal.gate.reg_mask   = ETH_REF_CLK_GATE,

#ifdef FH_CLK_GATE_DEFAULT
    .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.gate.sw_status   = CLK_UNGATE,
#endif

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE | PRE_DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV6,
    .clk.peri.obj.normal.div.reg_mask     = 0x0f << 24,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 5,
#endif

    .clk.peri.obj.normal.div.pdiv_value = 2,

    .update_func = eth_ref_clk_update,
};

static struct fh_clk wdt_clk = {
    .name  = "wdt_clk",
    .level = LEVEL_PERIPHERAL,
    //	//.parent = &clk_in,
    .clk.peri.peri_flag               = LEVEL_PERI_NORMAL,
    .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX,

    // gate
    .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE,

    // div
    .clk.peri.obj.normal.div.div_flag     = DIV_ENABLE,
    .clk.peri.obj.normal.div.reg_offset   = REG_PMU_CLK_DIV5,
    .clk.peri.obj.normal.div.reg_mask     = 0x3f << 8,
    .clk.peri.obj.normal.div.sw_div_multi = 1,

#ifdef FH_CLK_DIV_DEFAULT
    .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE,
#else
    .clk.peri.obj.normal.div.sw_div_value = 29,
#endif

    .update_func = wdt_clk_update,
};

struct fh_clk* fh_clk_array[] = {
    &clk_in, &cis_pclk, &pll0, &pll1, &ddr_clk_normal, &ddr_clk_div2, &cpu_fclk,
    &cpu_aclk, &cpu_hclk, &cpu_pclk, &isp_aclk, &vcu_clk, &vou_clk,
    &mipi_p32_clk, &cis_clk_out, &pts_clk, &mipi_pix_clk, &pix_clk,

    // pll1
    &sdc0_clk2x, &sdc0_clk, &sdc0_clk_out, &sdc0_clk_sample, &sdc0_clk_drive,

    &sdc1_clk2x, &sdc1_clk, &sdc1_clk_out, &sdc1_clk_sample, &sdc1_clk_drive,

    &spi0_clk, &spi1_clk, &mipi_dphy_clk20m, &i2c0_clk, &i2c1_clk, &uart0_clk,
    &uart1_clk, &pwm_clk, &time0_clk, &time1_clk, &sadc_clk, &eth_ref_clk,

    &wdt_clk,

};

static inline rt_int32_t wrap_read_reg(rt_uint32_t offset, rt_uint32_t mask,
                                       rt_uint32_t* value)
{
    rt_uint32_t temp_v, temp_shift;

    /*	if(fh_pmu_status() == PMU_STATUS_CLOSE)
     return -1;*/
    temp_v = FH_TIMER_READL(offset);
    temp_v &= mask;
    temp_shift = __rt_ffs(mask);
    temp_v     = temp_v >> (temp_shift - 1);
    *value     = temp_v;
    return 0;
}

static inline rt_int32_t wrap_write_reg(rt_uint32_t offset, rt_uint32_t mask,
                                        rt_uint32_t value)
{
    rt_uint32_t temp_v, temp_shift;

    /*
     if(fh_pmu_status() == PMU_STATUS_CLOSE)
     return -1;
     */

    temp_v = FH_TIMER_READL(offset);
    temp_v &= ~mask;
    temp_shift = __rt_ffs(mask);
    temp_v |= value << (temp_shift - 1);
    FH_TIMER_WRITEL(offset, temp_v);
    return 0;
}

rt_int32_t check_pix_clk_source(rt_uint32_t offset, rt_uint32_t mask,
                                rt_uint32_t* value)
{
    rt_uint32_t mux0;
    rt_int32_t ret;
    ret = wrap_read_reg(offset, mask, &mux0);

    if (ret != 0)
    {
        return ret;
    }

    *value = mux0;
    return 0;
}

rt_int32_t check_xtal_pll0(rt_uint32_t offset, rt_uint32_t mask,
                           rt_uint32_t* value)
{
    rt_uint32_t mux0;
    rt_int32_t ret;
    ret = wrap_read_reg(offset, mask, &mux0);

    if (ret != 0)
    {
        return ret;
    }
    if (mux0 == MUX0_PLL0_CLK)
        *value = MUX0_PLL0_CLK;
    else
        *value = MUX0_XTAL_CLK;

    return 0;
}

void cal_pll0_prediv(rt_uint32_t* div_flag, rt_uint32_t* pre_value)
{
    if (!(*div_flag & PRE_DIV_CAL_ALREADY))
    {
        // before has got the prediv value..
        if (*div_flag & PRE_DIV_ENABLE)
        {
            *pre_value *= 2;
        }
        else
        {
            *pre_value = 2;
        }
        *div_flag |= PRE_DIV_ENABLE | PRE_DIV_CAL_ALREADY;
    }
}

rt_int32_t sw_div_process(rt_uint32_t div_flag, rt_uint32_t offset,
                          rt_uint32_t mask, rt_uint32_t* div_value)
{
    // rt_kprintf("----------div go----------\n");
    rt_uint32_t div;
    rt_int32_t ret;
    if (div_flag & DIV_ENABLE)
    {
        ret = wrap_read_reg(offset, mask, &div);
        if (ret != 0)
        {
            return ret;
        }

        //
        //		rt_kprintf("hw value is %x\n",div);
        //		rt_kprintf("sw value is %x\n",div_value);
        //
        //		rt_kprintf("offset is %x,value :%x\n",offset +
        //0xf0000000,*(rt_uint32_t*)(offset + 0xf0000000));
        //		rt_kprintf("mask is %x\n",mask);

        // if use the hw default value....

        if (*div_value == FH_CLK_DIV_DEFAULT_VALUE)
        {
            *div_value = div;
            return 0;
        }

        if (div != *div_value)
        {
            ret = wrap_write_reg(offset, mask, *div_value);
            if (ret != 0)
            {
                return ret;
            }
        }
    }
    // rt_kprintf("----------div done----------\n");
    return 0;
    //*div_flag |= PRE_DIV_ENABLE;
}

void cal_baud_hz(rt_uint32_t clk_in, rt_uint32_t div_flag, rt_uint32_t pre_div,
                 rt_uint32_t div, rt_uint32_t div_multi, rt_uint32_t* baud_out)
{
    // div += 1;
    if (div_flag & PRE_DIV_ENABLE)
    {
        *baud_out = (clk_in / pre_div);
    }
    else
    {
        *baud_out = clk_in;
    }

    if (div_flag & DIV_ENABLE)
    {
        *baud_out /= ((div + 1) * div_multi);
    }
}

void cal_baud_div(rt_uint32_t clk_in, rt_uint32_t div_flag, rt_uint32_t pre_div,
                  rt_uint32_t* div, rt_uint32_t div_multi, rt_uint32_t baud_out)
{
    // div += 1;
    rt_uint32_t temp_baud_hz, temp_baud_div;

    if (div_flag & DIV_ENABLE)
    {
        if (div_flag & PRE_DIV_ENABLE)
        {
            temp_baud_hz = (clk_in / pre_div);
        }
        else
        {
            temp_baud_hz = clk_in;
        }
        temp_baud_div = temp_baud_hz / baud_out;
        *div          = temp_baud_div - 1;
    }
}

rt_int32_t process_gate(rt_uint32_t gate_flag, rt_uint32_t reg_offset,
                        rt_uint32_t reg_mask, rt_uint32_t* sw_status,
                        rt_uint32_t* pclk_status)
{
    // rt_kprintf("----------gate go----------\n");
    rt_uint32_t hw_gate;
    rt_int32_t ret;
    if (gate_flag == HAS_GATE)
    {
        ret = wrap_read_reg(reg_offset, reg_mask, &hw_gate);
        if (ret != 0)
        {
            return ret;
        }

        if (*sw_status == FH_CLK_GATE_DEFAULT_VALUE)
        {
            *sw_status   = hw_gate;
            *pclk_status = *sw_status;
            return 0;
        }

        //		rt_kprintf("gate hw is :%x\n",hw_gate);
        //		rt_kprintf("gate sw is :%x\n",sw_status);
        if (hw_gate != *sw_status)
        {
            // update the gate..
            //			rt_kprintf("gate reg offset is
            //:%x\n",reg_offset);
            //			rt_kprintf("gate reg mask is :%x\n",reg_mask);
            //			rt_kprintf("gate reg write is :%x\n",sw_status);
            ret = wrap_write_reg(reg_offset, reg_mask, *sw_status);
            if (ret != 0)
            {
                return ret;
            }
        }

        *pclk_status = *sw_status;
    }

    else
    {
        *pclk_status |= CLK_HAS_NO_GATE;
    }
    // rt_kprintf("---------gate done---------\n");
    return 0;
}

void clk_handle(struct fh_clk* p_clk, struct fh_clk* parent)
{
    // rt_uint32_t div;
    // rt_uint32_t sw_gate;
    rt_uint32_t phase;
    rt_int32_t ret;
    p_clk->parent = parent;
    //	switch
    // fh_clk_debug(p_clk,"----parent----\t ----clk out rate----\n ");
    if (p_clk->parent)
        // rt_kprintf("%-8.*s 0x%02x", RT_NAME_MAX, thread->name,
        // thread->current_priority);
        fh_clk_debug(p_clk, "parent:'%s'\n", p_clk->parent->name);
    else
        fh_clk_debug(p_clk, "'root node'\n");

    switch (p_clk->level)
    {
    case LEVEL_CRYSTAL:
        // fh_clk_debug(p_clk,"clk out:%d\n",p_clk->clk_out_rate);
        break;
    case LEVEL_PLL:
        // fh_clk_debug(p_clk,"%d\n",p_clk->clk_out_rate);
        break;
    case LEVEL_PERIPHERAL:

        switch (p_clk->clk.peri.peri_flag)
        {
        case LEVEL_PERI_NORMAL:
            // div = p_clk->clk.peri.obj.normal.div.sw_div_value;
            ret = sw_div_process(p_clk->clk.peri.obj.normal.div.div_flag,
                                 p_clk->clk.peri.obj.normal.div.reg_offset,
                                 p_clk->clk.peri.obj.normal.div.reg_mask,
                                 &p_clk->clk.peri.obj.normal.div.sw_div_value);

            if (ret != 0)
            {
                fh_clk_err(p_clk, "div process failed.error no:%x\n", ret);
                break;
            }

            // fh_clk_debug(p_clk,"hw div is
            // %d\n",p_clk->clk.peri.obj.ddr.div.hw_div_value);
            //			fh_clk_debug(p_clk,"sw div is
            //%d\n",p_clk->clk.peri.obj.normal.div.sw_div_value);
            //			fh_clk_debug(p_clk,"pre div is
            //%d\n",p_clk->clk.peri.obj.normal.div.pdiv_value);
            //			fh_clk_debug(p_clk,"clk in is
            //%d\n",p_clk->parent->clk_out_rate);
            //			fh_clk_debug(p_clk,"peri flag is
            //%x\n",p_clk->clk.peri.obj.normal.div.div_flag);
            // hw will self add 1..

            cal_baud_hz(p_clk->parent->clk_out_rate,
                        p_clk->clk.peri.obj.normal.div.div_flag,
                        p_clk->clk.peri.obj.normal.div.pdiv_value,
                        p_clk->clk.peri.obj.normal.div.sw_div_value,
                        p_clk->clk.peri.obj.normal.div.sw_div_multi,
                        &p_clk->clk_out_rate);

            // fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate);
            // fix the gate..
            // sw_gate = p_clk->clk.peri.obj.normal.gate.sw_status;
            ret = process_gate(p_clk->clk.peri.obj.normal.gate.gate_flag,
                               p_clk->clk.peri.obj.normal.gate.reg_offset,
                               p_clk->clk.peri.obj.normal.gate.reg_mask,
                               &p_clk->clk.peri.obj.normal.gate.sw_status,
                               &p_clk->gate);

            if (ret != 0)
            {
                fh_clk_err(p_clk, "gate process failed.error no:%x\n", ret);
                break;
            }

            break;
        case LEVEL_PERI_DDR:
            // rt_uint32_t mux0,mux1;
            // div = p_clk->clk.peri.obj.ddr.div.sw_div_value;

            ret = sw_div_process(p_clk->clk.peri.obj.ddr.div.div_flag,
                                 p_clk->clk.peri.obj.ddr.div.reg_offset,
                                 p_clk->clk.peri.obj.ddr.div.reg_mask,
                                 &p_clk->clk.peri.obj.ddr.div.sw_div_value);

            if (ret != 0)
            {
                fh_clk_err(p_clk, "div process failed.error no:%x\n", ret);
                break;
            }

            //			fh_clk_debug(p_clk,"sw div is
            //%d\n",p_clk->clk.peri.obj.ddr.div.sw_div_value);
            //			fh_clk_debug(p_clk,"pre div is
            //%d\n",p_clk->clk.peri.obj.ddr.div.pdiv_value);
            //			fh_clk_debug(p_clk,"clk in is
            //%d\n",p_clk->parent->clk_out_rate);
            //			fh_clk_debug(p_clk,"peri flag is
            //%x\n",p_clk->clk.peri.obj.ddr.div.div_flag);

            cal_baud_hz(p_clk->parent->clk_out_rate,
                        p_clk->clk.peri.obj.ddr.div.div_flag,
                        p_clk->clk.peri.obj.ddr.div.pdiv_value,
                        p_clk->clk.peri.obj.ddr.div.sw_div_value,
                        p_clk->clk.peri.obj.ddr.div.sw_div_multi,
                        &p_clk->clk_out_rate);

            // fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate);
            // fix the gate..
            // fh_clk_debug(p_clk,"gate reg add is:%x\t mask
            // is:%x\n",p_clk->clk.peri.obj.ddr.gate.reg_offset,p_clk->clk.peri.obj.ddr.gate.reg_mask);
            // sw_gate = p_clk->clk.peri.obj.ddr.gate.sw_status;

            ret = process_gate(p_clk->clk.peri.obj.ddr.gate.gate_flag,
                               p_clk->clk.peri.obj.ddr.gate.reg_offset,
                               p_clk->clk.peri.obj.ddr.gate.reg_mask,
                               &p_clk->clk.peri.obj.ddr.gate.sw_status,
                               &p_clk->gate);

            if (ret != 0)
            {
                fh_clk_err(p_clk, "gate process failed.error no:%x\n", ret);
                break;
            }

            break;
        case LEVEL_PERI_SDC:
            // just need to handle the phase....
            p_clk->clk_out_rate = p_clk->parent->clk_out_rate;
            if (p_clk->clk.peri.obj.sdc.phase_diff & DIFF_REFERENCE)
            {
                // fh_clk_debug(p_clk,"this is the reference..no need to
                // process..\n");
                break;
            }

            // baud ...

            // phase..
            // fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate);
            // hw status..
            ret = wrap_read_reg(p_clk->clk.peri.obj.sdc.reg_offset,
                                p_clk->clk.peri.obj.sdc.reg_mask, &phase);

            if (ret != 0)
            {
                fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret);
                break;
            }

            //			fh_clk_debug(p_clk,"hw phase is :%x\n",phase);
            //			fh_clk_debug(p_clk,"sw phase is
            //:%x\n",p_clk->clk.peri.obj.sdc.phase_diff);
            if (phase != p_clk->clk.peri.obj.sdc.phase_diff)
            {
                // update the hw para..
                ret = wrap_write_reg(p_clk->clk.peri.obj.sdc.reg_offset,
                                     p_clk->clk.peri.obj.sdc.reg_mask,
                                     p_clk->clk.peri.obj.sdc.phase_diff);
                if (ret != 0)
                {
                    fh_clk_err(p_clk, "write pmu failed.error no:%x\n", ret);
                    break;
                }
            }

            break;
        case LEVEL_PERI_GMAC:
            break;
        default:
            break;
        }
    }

    fh_clk_debug(p_clk, "clk out:%d\n", p_clk->clk_out_rate);
}

//
void clk_in_update(struct fh_clk* p_clk) { clk_handle(p_clk, RT_NULL); }
void cis_pclk_update(struct fh_clk* p_clk) { clk_handle(p_clk, RT_NULL); }
void pll1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &clk_in); }
void pll0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &clk_in); }
void ddr_clk_update(struct fh_clk* p_clk)
{
    // check if pll0 or pll1
    rt_uint32_t mux0, mux1;
    rt_int32_t ret;
    struct fh_clk* parent;
    // 1 step: fix the parent..
    ret = wrap_read_reg(p_clk->clk.peri.obj.ddr.mux[1].reg_offset,
                        p_clk->clk.peri.obj.ddr.mux[1].reg_mask, &mux1);
    if (ret != 0)
    {
        fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret);
        return;
    }

    if (mux1 == MUX1_PLL0_CLK)
    {
        ret = check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset,
                              p_clk->clk.peri.obj.ddr.mux[0].reg_mask, &mux0);
        if (ret != 0)
        {
            fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret);
            return;
        }
        if (mux0 == MUX0_PLL0_CLK)
        {
            // ddr normal parent is pll0
            parent = &pll0;
        }
        else
        {
            // ddr normal parent is xtal
            parent = &clk_in;
        }
    }
    else
    {
        // ddr normal parent is pll1
        parent = &pll1;
    }
    p_clk->clk.peri.obj.ddr.mux[0].mux_flag = HAS_MUX;
    p_clk->clk.peri.obj.ddr.mux[1].mux_flag = HAS_MUX;
    clk_handle(p_clk, parent);
}

void fclk_update(struct fh_clk* p_clk)
{
    // check if pll0 or xtal
    rt_uint32_t mux0;
    rt_int32_t ret;
    struct fh_clk* parent;  // 1 step: fix the parent..

    // mux0 =
    // check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset,p_clk->clk.peri.obj.ddr.mux[0].reg_mask);
    ret = check_xtal_pll0(p_clk->clk.peri.obj.normal.mux.reg_offset,
                          p_clk->clk.peri.obj.normal.mux.reg_mask, &mux0);
    if (ret != 0)
    {
        fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret);
        return;
    }

    // fh_clk_debug(p_clk,"mux0 wrap value is %x\n",mux0);
    if (mux0 == MUX0_PLL0_CLK)
    {
        // ddr normal parent is pll0
        parent = &pll0;
    }
    else
    {
        // ddr normal parent is xtal
        parent = &clk_in;
    }
    p_clk->clk.peri.obj.normal.mux.mux_flag = HAS_MUX;
    // 2 step:fix the div...
    if (mux0 == MUX0_PLL0_CLK)
    {
        // cal_pll0_prediv(&p_clk->clk.peri.obj.ddr.div.div_flag,&p_clk->clk.peri.obj.ddr.div.pdiv_value);
        cal_pll0_prediv(&p_clk->clk.peri.obj.normal.div.div_flag,
                        &p_clk->clk.peri.obj.normal.div.pdiv_value);
    }
    clk_handle(p_clk, parent);
}

void pix_update(struct fh_clk* p_clk)
{
    // check if pll0 or xtal
    rt_uint32_t mux0;
    rt_int32_t ret;
    struct fh_clk* parent;  // 1 step: fix the parent..
#if (1)
    // mux0 =
    // check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset,p_clk->clk.peri.obj.ddr.mux[0].reg_mask);
    ret = check_pix_clk_source(p_clk->clk.peri.obj.normal.mux.reg_offset,
                               p_clk->clk.peri.obj.normal.mux.reg_mask, &mux0);
    if (ret != 0)
    {
        fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret);
        return;
    }
    //#define CIS_PIX_CLK				(0)
    //#define CIS_PIX_CLK_OPPOSITE	(1)
    //#define MIPI_PIX_CLK			(2)

    // fh_clk_debug(p_clk,"mux0 wrap value is %x\n",mux0);
    if (mux0 == CIS_PIX_CLK || mux0 == CIS_PIX_CLK_OPPOSITE)
    {
        // ddr normal parent is pll0
        parent = &cis_pclk;
    }
    else
    {
        parent = &mipi_pix_clk;
    }
    p_clk->clk.peri.obj.normal.mux.mux_flag = HAS_MUX;

#endif
    clk_handle(p_clk, parent);
}

void aclk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &cpu_fclk); }
void hclk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void pclk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &cpu_hclk); }
void isp_aclk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void vcu_clk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void vou_clk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void mipi_p32_clk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void cis_clk_out_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void pts_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void mipi_pix_clk_update(struct fh_clk* p_clk) { fclk_update(p_clk); }
void spi0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void spi1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void mipi_dphy_clk20m_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void i2c0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void i2c1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void uart0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void pwm_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void time0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void time1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void uart1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void sadc_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
// sdc0...
void sdc0_clk2x_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void sdc0_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &sdc0_clk2x); }
void sdc0_clk_out_update(struct fh_clk* p_clk) { clk_handle(p_clk, &sdc0_clk); }
void sdc0_clk_sample_update(struct fh_clk* p_clk)
{
    clk_handle(p_clk, &sdc0_clk2x);
}

void sdc0_clk_drv_update(struct fh_clk* p_clk)
{
    clk_handle(p_clk, &sdc0_clk2x);
}

void sdc1_clk2x_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void sdc1_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &sdc1_clk2x); }
void sdc1_clk_out_update(struct fh_clk* p_clk) { clk_handle(p_clk, &sdc1_clk); }
void sdc1_clk_sample_update(struct fh_clk* p_clk)
{
    clk_handle(p_clk, &sdc1_clk2x);
}

void sdc1_clk_drv_update(struct fh_clk* p_clk)
{
    clk_handle(p_clk, &sdc1_clk2x);
}

void eth_ref_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &pll1); }
void wdt_clk_update(struct fh_clk* p_clk) { clk_handle(p_clk, &cpu_pclk); }
/**
 * @brief System Clock Configuration
 */
#define CLK_CONTROL_BASE PMU_REG_BASE
void rt_hw_clock_init(void)
{
    struct fh_clk* p;
    int i;
    fh_clk_tree.c_base_addr = CLK_CONTROL_BASE;
    fh_clk_tree.clk_head    = fh_clk_array;

    // first open all the clock..
    FH_TIMER_WRITEL(REG_PMU_CLK_GATE, 0x0);
    for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk*); i++)
    {
        p = fh_clk_tree.clk_head[i];
        if (p->update_func) p->update_func(p);
    }
}

/***************
 *
 * new add
 *
 **************/

/* clocks cannot be de-registered no refcounting necessary */
struct fh_clk* clk_get(const char* name)
{
    struct fh_clk* p;
    int i;

    for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk*); i++)
    {
        p = fh_clk_tree.clk_head[i];
        if (!rt_strcmp(p->name, name))
        {
            return p;
        }
    }

    return RT_NULL;
}

//
//#define HAS_GATE			(0)
//#define HAS_NO_GATE			(1)
//	rt_uint32_t gate_flag;
//#define CLK_UNGATE			(0)
//#define CLK_GATE			(1)

void clk_gate_control(struct fh_clk* p_clk, rt_uint32_t status)
{
    if (status > CLK_GATE) return;

    if (p_clk->level == LEVEL_PERIPHERAL)
    {
        switch (p_clk->clk.peri.peri_flag)
        {
        case LEVEL_PERI_NORMAL:

            if (p_clk->clk.peri.obj.normal.gate.gate_flag == HAS_GATE)
            {
                p_clk->clk.peri.obj.normal.gate.sw_status = status;
            }
            else
            {
                rt_kprintf("[%-16.15s]: no gate...\t\n", p_clk->name);
            }

            break;
        case LEVEL_PERI_DDR:
            if (p_clk->clk.peri.obj.ddr.gate.gate_flag == HAS_GATE)
            {
                p_clk->clk.peri.obj.ddr.gate.sw_status = status;
            }
            else
            {
                rt_kprintf("[%-16.15s]: no gate...\t\n", p_clk->name);
            }

            break;

        default:
            break;
        }

        p_clk->update_func(p_clk);
    }
}

void clk_gate(struct fh_clk* p_clk) { clk_gate_control(p_clk, CLK_GATE); }
void clk_ungate(struct fh_clk* p_clk) { clk_gate_control(p_clk, CLK_UNGATE); }
rt_uint32_t clk_get_rate(struct fh_clk* p_clk)
{
    rt_uint32_t rate;
    // first update the status
    p_clk->update_func(p_clk);
    rate = p_clk->clk_out_rate;
    return rate;
}

void clk_set_rate(struct fh_clk* p_clk, rt_uint32_t rate_value)
{
    rt_uint32_t clk_in, div_flag, pre_div, div_multi, baud_out;

    if (p_clk->level == LEVEL_PERIPHERAL)
    {
        switch (p_clk->clk.peri.peri_flag)
        {
        case LEVEL_PERI_NORMAL:

            clk_in    = p_clk->parent->clk_out_rate;
            div_flag  = p_clk->clk.peri.obj.normal.div.div_flag;
            pre_div   = p_clk->clk.peri.obj.normal.div.pdiv_value;
            div_multi = p_clk->clk.peri.obj.normal.div.sw_div_multi;
            baud_out  = rate_value;

            cal_baud_div(clk_in, div_flag, pre_div,
                         &p_clk->clk.peri.obj.normal.div.sw_div_value,
                         div_multi, baud_out);

            break;
        case LEVEL_PERI_DDR:
            // rt_uint32_t mux0,mux1;
            clk_in    = p_clk->parent->clk_out_rate;
            div_flag  = p_clk->clk.peri.obj.ddr.div.div_flag;
            pre_div   = p_clk->clk.peri.obj.ddr.div.pdiv_value;
            div_multi = p_clk->clk.peri.obj.ddr.div.sw_div_multi;
            baud_out  = rate_value;

            cal_baud_div(clk_in, div_flag, pre_div,
                         &p_clk->clk.peri.obj.ddr.div.sw_div_value, div_multi,
                         baud_out);
            break;
        case LEVEL_PERI_SDC:
            fh_clk_debug(p_clk,
                         "sdc can't set baud,please set the 'sdcx_clk2x'\n");
            break;
        case LEVEL_PERI_GMAC:
            fh_clk_debug(p_clk, "gmac not support set baud\n");
            break;
        default:
            break;
        }
        p_clk->update_func(p_clk);
    }
}

rt_uint32_t sdc_get_phase(struct fh_clk* p_clk)
{
    if (p_clk->level == LEVEL_PERIPHERAL)
    {
        if (p_clk->clk.peri.peri_flag == LEVEL_PERI_SDC)
        {
            p_clk->update_func(p_clk);
            return p_clk->clk.peri.obj.sdc.phase_diff;
        }
    }
    return SDC_CLK_PARA_ERROR;
}

rt_uint32_t sdc_set_phase(struct fh_clk* p_clk, rt_uint32_t phase)
{
    if (phase > DIFF_SDC_REFCLK_270) return SDC_CLK_PARA_ERROR;

    if (p_clk->level == LEVEL_PERIPHERAL)
    {
        if (p_clk->clk.peri.peri_flag == LEVEL_PERI_SDC)
        {
            p_clk->clk.peri.obj.sdc.phase_diff = phase;
            p_clk->update_func(p_clk);
            return SDC_CLK_PARA_OK;
        }
    }
    return SDC_CLK_PARA_ERROR;
}

#ifdef FH_DBG_CLK
int fh_clk_nlist()
{
    struct fh_clk* p;
    int i;

    for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk*); i++)
    {
        p = fh_clk_tree.clk_head[i];
        // p->update_func(p);
        rt_kprintf("[%-16.15s]:\t\t[baud]:%d\t\n", p->name, p->clk_out_rate);
    }

    return 0;
}

int fh_clk_glist()
{
    struct fh_clk* p;
    int i;
    rt_kprintf("first bit set means has no gate..\n");
    for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk*); i++)
    {
        p = fh_clk_tree.clk_head[i];
        // p->update_func(p);
        if (!(p->gate & CLK_HAS_NO_GATE))
            rt_kprintf("[%-16.15s]:\t\t[gate]:%d\t\n", p->name, p->gate);
        else
            rt_kprintf("[%-16.15s]:\t\t[gate]:no gate..\t\n", p->name);
    }

    return 0;
}
#endif

#ifdef RT_USING_FINSH
#include <finsh.h>
#ifdef FH_DBG_CLK
FINSH_FUNCTION_EXPORT(fh_clk_nlist, fh_clk_name_list..);
FINSH_FUNCTION_EXPORT(fh_clk_glist, fh_clk_gate_list..);
#endif
#endif
